:- module(notify_ports).
:- export foreachnotification_ / 8.
:- export foreachnotification_ / 7.
:- export foreachnotification / 7.
:- export receive_notifications / 4.
:- export open_receiver_init / 6.
:- export open_receiver / 4.
:- export send_notification / 3.
:- export close_sender / 2.
:- export open_sender / 2.
:- export foreachnotification / 6.
:- export receive_notifications / 3.
:- export open_receiver_init / 4.
:- export open_receiver / 2.
:- export send_notification / 2.
:- export close_sender / 1.
:- export open_sender / 1.
:- comment(summary, "One-to-many notification ports").
:- comment(author, "Joachim Schimpf").
:- comment(date, "$Date: 2004/12/03 15:28:53 $").
:- comment(copyright, "Imperial College London and Parc Technologies").
:- comment(desc, html("\n    This library implements a nonlogical feature, called notification\n    ports. It is a one-to-many form of messaging, i.e. there\n    is one send port and possibly many attached receive ports.\n    Both send and receive ports have unique handles, which is the\n    nonlogical bit. Apart from that, the message stream is just an\n    infinite list, with the sender extending the list at the tail and\n    the receivers each individually progressing through the list.\n    <P>\n    Straightforward interface:\n    <PRE>\n\topen_sender(-Sender)\n\tclose_sender(+Sender)\n\tsend_notification(+Sender, +Message)\n\topen_receiver(+Sender, -Receiver)\n\topen_receiver_init(+Sender, +InitMsgs, -InitMsgsTail, -Receiver)\n\treceive_notifications(+Receiver, -Messages, -Status)\n\tforeachnotification(+BaseName, -Message, +Params, +Receiver, -Status, +Goals)\n    </PRE>\n    There is also a slightly more memory efficient API where sender and\n    receiver can be fields of larger structures rather than separate\n    substructures. These  larger structures must always be created\n    by the caller (in the case of the sender this is often an attribute\n    structure, in the case of the receiver it is sometimes advantageous\n    to package a suspension together with the receiver in order to kill\n    it at the end of all messages):\n    <PRE>\n\topen_sender(+SendPos, +SendStruct)\n\tclose_sender(+SendPos, +SendStruct)\n\tsend_notification(+SendPos, +SendStruct, +Message)\n\topen_receiver(+SendPos, +SendStruct, +ReceivePos, +ReceiveStruct)\n\topen_receiver_init(+SendPos, +SendStruct, +InitMsgs, -InitMsgsTail,\n\t\t\t\t+ReceivePos, +ReceiveStruct)\n\treceive_notifications(+ReceivePos, +ReceiveStruct, -Messages, -Status)\n\tforeachnotification(+BaseName, -Message, +Params, +ReceivePos, +ReceiveStruct, -Status, +Goals)\n    </PRE>\n    ")).
:- comment(eg, "\n    % This example shows a typical use of notification ports.\n    % A notification port is used in addition to a waking list\n    % in order to transfer precise information about the reason for waking.\n\n    % We define a variable attribute (myattr) consisting of a send port\n    % and a waking list. \n\n\n    :- lib(notify_ports).\n\n    :- meta_attribute(myattr, []).\n    :- local struct(myattr(port,susplist)).\n    :- local struct(myrec(port,susp)).\n\n\n    test :-\n\t    init_var(X),\n\t    log_all_messages(X),\n\t    touch_var(X, hello),\n\t    touch_var(X, out),\n\t    touch_var(X, there),\n\t    fini_var(X).\n\n\n    % initialise and attach our attribute to the given variable\n    init_var(X) :-\n\t    Attr = myattr{},\n\t    open_sender(port of myattr, Attr),\n\t    init_suspension_list(susplist of myattr, Attr),\n\t    add_attribute(X, Attr, myattr).\n\n\n    % simulate an action on the variable: send a message and wake\n    touch_var(_X{myattr:Attr}, Message) ?-\n\t    send_notification(port of myattr, Attr, Message),\n\t    schedule_suspensions(susplist of myattr, Attr),\n\t    wake.\n\n    % finalise the attribute, e.g. before the variable gets instantiated\n    fini_var(_X{myattr:Attr}) ?-\n            close_sender(port of myattr, Attr),\n\t    schedule_suspensions(susplist of myattr, Attr),\n\t    wake.\n\n    % a sample demon that will report every time the variable is touched\n    log_all_messages(X{myattr:Attr}) ?-\n\t    Receiver = myrec{susp:Susp},\n\t    open_receiver(port of myattr, Attr, port of myrec, Receiver),\n\t    suspend(log_demon(Receiver), 2, X->myattr:(susplist of myattr), Susp).\n\n    :- demon log_demon/1.\n    log_demon(Receiver) :-\n\t    foreachnotification(log, Message, [], port of myrec, Receiver, Status, (\n\t\twriteln(received(Message))\n\t    )),\n\t    ( Status = closed ->\n\t\targ(susp of myrec, Receiver, Susp),\n\t\tkill_suspension(Susp),\n\t\twriteln(closed)\n\t    ;\n\t\ttrue\n\t    ).\n    ").
:- comment(close_sender / 1, [summary : "Close a send port", amode : close_sender(+), args : ["SendPort" : "a send port structure"]]).
:- comment(close_sender / 2, [summary : "Close a send port on a structure field", amode : close_sender(+, +), args : ["Pos" : "positive integer, the send port's field number in Struct", "Struct" : "a structure whose Pos-th field is a send port"]]).
:- comment(foreachnotification / 6, [summary : "A control construct to iterate over received notifications", amode : foreachnotification(+, -, +, +, -, +), args : ["BaseName" : "an atom used as the basename for the generated auxiliary predicate", "Message" : "a variable", "Params" : "a list of global variables in the iteration body (as in do/2)", "ReceivePort" : "a receiver structure as created by open_receiver", "Status" : "a variable, will be bound to 'open' or 'closed'", "Goals" : "the goals that will be called for each iteration"], desc : html("\n    This is a control construct iterating over the currently available\n    messages on the given receive port. The purpose is to process the\n    received messages one by one without the need to create an auxiliary\n    list of received messages. The iteration terminates when there are\n    (currently) no more message on the receive port.\n    <P>\n    When the iteration terminates, the Status argument indicates whether\n    the associated sender is still open ('open') or has been closed\n    ('closed'). If closed, no more messages will arrive on this receive\n    port in the future.\n    <P>\n\n    "), see_also : [receive_notifications / 3], eg : "\n\tprocess_all_messages(ReceivePort, Log) :-\n\t    foreachnotification(sample, Message, [Log], ReceivePort, Status, (\n\t\twriteln(Log, received(Message)),\n\t\tdo_something(Message)\n\t    )),\n\t    ( Status = closed ->\n\t\twriteln(Log, end_of_messages)\n\t    ;\n\t\twriteln(Log, more_coming)\n\t    ).\n    "]).
:- comment(foreachnotification / 7, [summary : "A control construct to iterate over received notifications", amode : foreachnotification(+, -, +, +, +, -, +), args : ["BaseName" : "an atom used as the basename for the generated auxiliary predicate", "Message" : "a variable", "Params" : "a list of global variables in the iteration body (as in do/2)", "ReceivePos" : "positive integer, the send port's field number in ReceiveStruct", "ReceiveStruct" : "a structure", "Status" : "a variable, will be bound to 'open' or 'closed'", "Goals" : "the goals that will be called for each iteration"], desc : html("\n    This is a control construct iterating over the currently available\n    messages on the given receive port. The purpose is to process the\n    received messages one by one without the need to create an auxiliary\n    list of received messages. The iteration terminates when there are\n    (currently) no more message on the receive port.\n    <P>\n    When the iteration terminates, the Status argument indicates whether\n    the associated sender is still open ('open') or has been closed\n    ('closed'). If closed, no more messages will arrive on this receive\n    port in the future.\n    <P>\n\n    "), see_also : [receive_notifications / 4], eg : "see general example for the library"]).
:- comment(open_receiver / 2, [summary : "Create a receiver for a given notification sender", amode : open_receiver(+, -), args : ["SendPort" : "a send port structure", "ReceivePort" : "a variable, will be bound to a structure"], desc : html("\n    This predicate creates a receive port listening to messages sent\n    via the specified send port.  The new receive port will receive\n    all messages that are sent via the send port after the receiver\n    has been opened.  Messages that were sent before the receiver has\n    been opened will not be received by this receiver.\n    "), see_also : [open_receiver_init / 4]]).
:- comment(open_receiver / 4, [summary : "Create a receiver for a given notification sender", amode : open_receiver(+, +, +, +), args : ["SendPos" : "positive integer, the send port's field number in SendStruct", "SendStruct" : "a structure whose SendPos-th field is a send port", "ReceivePos" : "positive integer, the send port's field number in ReceiveStruct", "ReceiveStruct" : "a structure with free field ReceivePos"], desc : html("\n    SendPos and SendStruct identify a send port.  This predicate\n    creates a receive port listening to messages sent via the\n    specified send port.  The new receive port will receive all\n    messages that are sent via the send port after the receiver has\n    been opened.  Messages that were sent before the receiver has been\n    opened will not be received by this receiver.\n    <P>\n    The receiver will be installed in field ReceivePos of the structure\n    ReceiveStruct.\n    "), see_also : [open_receiver_init / 6]]).
:- comment(open_receiver_init / 4, [summary : "Create a receiver for a given notification sender", amode : open_receiver_init(+, +, -, -), args : ["SendPort" : "a send port structure", "InitialMessages" : "the head of a list of initial messages", "InitialMessagesTail" : "the tail of the list of initial messages", "ReceivePort" : "a variable, will be bound to a structure"], desc : html("\n    This predicate creates a receive port listening to messages sent\n    via the specified send port.  The new receive port will receive\n    all messages that are sent via the send port after the receiver\n    has been opened.  Messages that were sent before the receiver has\n    been opened will not be received by this receiver.\n    <P>\n    In addition to open_receiver/2, there is a difference list pair\n    (InitialMessages and InitialMessagesTail) which can be used to\n    fake a sequence of initial message that will be received on\n    the receive port without actually having been sent from the\n    associated send port. This feature can be used to bring the\n    message receiving agent into a particular starting state.\n    "), see_also : [open_receiver / 2]]).
:- comment(open_receiver_init / 6, [summary : "Create a receiver for a given notification sender", amode : open_receiver_init(+, +, +, -, +, +), args : ["SendPos" : "positive integer, the send port's field number in SendStruct", "SendStruct" : "a structure whose SendPos-th field is a send port", "InitialMessages" : "the head of a list of initial messages", "InitialMessagesTail" : "the tail of the list of initial messages", "ReceivePos" : "positive integer, the send port's field number in ReceiveStruct", "ReceiveStruct" : "a structure with free field ReceivePos"], desc : html("\n    SendPos and SendStruct identify a send port.  This predicate\n    creates a receive port listening to messages sent via the\n    specified send port.  The new receive port will receive all\n    messages that are sent via the send port after the receiver has\n    been opened.  Messages that were sent before the receiver has been\n    opened will not be received by this receiver.\n    <P>\n    The receiver will be installed in field ReceivePos of the structure\n    ReceiveStruct.\n    <P>\n    In addition to open_receiver/4, there is a difference list pair\n    (InitialMessages and InitialMessagesTail) which can be used to\n    fake a sequence of initial message that will be received on\n    the receive port without actually having been sent from the\n    associated send port. This feature can be used to bring the\n    message receiving agent into a particular starting state.\n    "), see_also : [open_receiver / 4]]).
:- comment(open_sender / 1, [summary : "Create a send port", amode : open_sender(-), args : ["SendPort" : "a variable, will be bound to a structure"]]).
:- comment(open_sender / 2, [summary : "Initialise a structure field as a send port", amode : open_sender(+, +), args : ["Pos" : "positive integer, the send port's field number in Struct", "Struct" : "a structure (with arity Pos or more)"]]).
:- comment(receive_notifications / 3, [summary : "Receive a list of currently available notification messages", amode : receive_notifications(+, -, -), args : ["ReceivePort" : "a receiver structure as created by open_receiver", "Messages" : "a variable, will be bound to a list", "Status" : "a variable, will be bound to 'open' or 'closed'"], desc : html("\n    This predicate retrieves all the messages that are currently\n    available at the given receive port.  This means all messages that\n    were sent via the associated send port but have not yet been\n    retrieved from this receive port. The messages are listed in the\n    order in which they were sent.\n    <P>\n    The Status argument indicates whether the associated sender is still\n    open ('open') or has been closed ('closed'). If closed, no more\n    messages will arrive on this receive port in the future.\n    "), see_also : [foreachnotification / 6]]).
:- comment(receive_notifications / 4, [summary : "Receive a list of currently available notification messages", amode : receive_notifications(+, +, -, -), args : ["ReceivePos" : "positive integer, the send port's field number in ReceiveStruct", "ReceiveStruct" : "a structure", "Messages" : "a variable", "Status" : "a variable"], desc : html("\n    This predicate retrieves all the messages that are currently\n    available at the given receive port.  This means all messages that\n    were sent via the associated send port but have not yet been\n    retrieved from this receive port.\n    <P>\n    The Status argument indicates whether the associated sender is still\n    open ('open') or has been closed ('closed'). If closed, no more\n    messages will arrive on this receive port in the future.\n    "), see_also : [foreachnotification / 7]]).
:- comment(send_notification / 2, [summary : "Send a notification message", amode : send_notification(+, +), args : ["SendPort" : "a send port structure", "Message" : "arbitrary term"], fail_if : "Fails if SendPort is closed", desc : html("\n    Messages is an arbitrary term that gets send via the send port SendPort.\n    All receivers that have connected to this send port at the time of\n    sending will be able to receive the message.\n    ")]).
:- comment(send_notification / 3, [summary : "Send a notification message", amode : send_notification(+, +, +), args : ["Pos" : "positive integer, the send port's field number in Struct", "Struct" : "a structure whose Pos-th field is a send port", "Message" : "arbitrary term"], fail_if : "Fails if send port is closed", desc : html("\n    Pos and Struct identify a send port. Messages is an arbitrary\n    term that gets send via this port. All receivers that have connected\n    to this send port at the time of sending will be able to receive\n    the message.\n    ")]).
:- comment(foreachnotification_ / 8, hidden).
:- comment(foreachnotification_ / 7, hidden).
